﻿using System.Buffers;
using System.Runtime.InteropServices;
using System.Text;
using CodeAgent.Host.Agents;
using CodeAgent.Host.Context;
using CodeAgent.Host.Options;
using Docker.DotNet;
using Docker.DotNet.Models;

namespace CodeAgent.Host.Docker;

public sealed class DockerManager : IDisposable
{
    private readonly DockerClient _dockerClient;
    private bool _disposed;

    /// <summary>
    /// 初始化 DockerManager 实例，创建一个新的 DockerClient。
    /// </summary>
    public DockerManager()
    {
        // 默认连接到本地 Docker。获取当前操作系统
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && string.IsNullOrEmpty(DockerOptions.DockerHost))
        {
            _dockerClient = new DockerClientConfiguration()
                .CreateClient();
        }
        else
        {
            _dockerClient = new DockerClientConfiguration(new Uri(DockerOptions.DockerHost))
                .CreateClient();
        }
    }

    /// <summary>
    /// 拉取 Docker 镜像
    /// </summary>
    /// <param name="imageName">镜像名称 (如 "nginx:latest")</param>
    /// <param name="tag"></param>
    /// <param name="password"></param>
    /// <param name="email"></param>
    /// <param name="progress"></param>
    public async Task PullImageAsync(string imageName, string tag = "latest", string? userName = null,
        string? password = null,
        string? email = null, Action<JSONMessage>? progress = null)
    {
        Console.WriteLine($"开始拉取镜像: {imageName}");

        await _dockerClient.Images.CreateImageAsync(new ImagesCreateParameters()
        {
            FromImage = imageName,
            Tag = tag,
        }, new AuthConfig()
        {
            Username = userName,
            Password = password,
            Email = email
        }, new Progress<JSONMessage>((message => { progress?.Invoke(message); })));

        Console.WriteLine($"镜像拉取完成: {imageName}");
    }

    /// <summary>
    /// 创建并启动一个容器
    /// </summary>
    /// <param name="containerName">容器名称</param>
    /// <param name="imageName">镜像名称</param>
    public async Task<string> CreateAndStartContainerAsync(string containerName, string imageName)
    {
        Console.WriteLine($"开始创建容器: {containerName} 使用镜像: {imageName}");

        var response = await _dockerClient.Containers.CreateContainerAsync(new CreateContainerParameters
        {
            Image = imageName,
            Name = containerName,
            HostConfig = new HostConfig
            {
                AutoRemove = true // 容器停止后自动删除
            }
        });

        Console.WriteLine($"容器创建完成: {response.ID}");

        // 启动容器
        var started = await _dockerClient.Containers.StartContainerAsync(response.ID, null);
        if (!started)
        {
            throw new Exception($"容器启动失败: {response.ID}");
        }

        Console.WriteLine($"容器启动成功: {response.ID}");
        return response.ID;
    }

    /// <summary>
    /// 停止并删除一个容器
    /// </summary>
    /// <param name="containerId">容器 ID</param>
    public async Task StopAndRemoveContainerAsync(string containerId)
    {
        Console.WriteLine($"停止并删除容器: {containerId}");

        try
        {
            // 停止容器
            await _dockerClient.Containers.StopContainerAsync(containerId, new ContainerStopParameters());
            // 删除容器
            await _dockerClient.Containers.RemoveContainerAsync(containerId, new ContainerRemoveParameters
            {
                Force = true
            });

            Console.WriteLine($"容器已删除: {containerId}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"操作容器失败: {ex.Message}");
        }
    }

    /// <summary>
    /// 创建并启动一个容器，支持目录映射
    /// </summary>
    /// <param name="containerName">容器名称</param>
    /// <param name="imageName">镜像名称</param>
    /// <param name="hostPath">主机路径</param>
    /// <param name="containerPath">容器内路径</param>
    /// <param name="command">容器启动命令</param>
    /// <param name="workingDir">容器工作目录</param>
    /// <param name="port">容器工作端口</param>
    /// <param name="id"></param>
    public async Task<string> CreateAndStartContainerAsync(string containerName,
        string imageName,
        string? hostPath,
        string? containerPath = null,
        string[]? command = null,
        string? workingDir = null,
        string? port = null, string id = "")
    {
        Console.WriteLine($"开始创建容器: {containerName} 使用镜像: {imageName}");


        if (AgentContext.Current?.NotifyAction != null)
            await AgentContext.Current!.NotifyAction!.Invoke(new AgentConsoleProtocol
            {
                Id = id,
                type = "info",
                message = "开始创建容器，镜像：" + imageName,
                code = 200,
                error = null
            });

        var config = new CreateContainerParameters
        {
            Image = imageName,
            Name = containerName,
            HostConfig = new HostConfig
            {
                AutoRemove = true // 容器停止后自动删除
            }
        };

        // 如果提供了目录映射
        if (!string.IsNullOrEmpty(hostPath) && !string.IsNullOrEmpty(containerPath))
        {
            config.HostConfig.Binds = new List<string>
            {
                $"{hostPath}:{containerPath}"
            };
        }

        // 如果提供了端口映射
        if (!string.IsNullOrEmpty(port))
        {
            var ports = port.Split(':');

            // config需要配置导出端口，否则无法访问
            config.ExposedPorts = new Dictionary<string, EmptyStruct>
            {
                { ports[0], default }
            };

            // internal ports:external ports 绑定到容器本身的端口
            config.HostConfig.PortBindings = new Dictionary<string, IList<PortBinding>>
            {
                {
                    ports[0], new List<PortBinding>
                    {
                        new PortBinding
                        {
                            HostPort = ports[1],
                            HostIP = "0.0.0.0"
                        }
                    }
                }
            };
        }

        // 如果提供了启动命令
        if (command is { Length: > 0 })
        {
            config.Cmd = command;
        }

        // 如果提供了工作目录
        if (!string.IsNullOrEmpty(workingDir))
        {
            config.WorkingDir = workingDir;
        }

        var response = await _dockerClient.Containers.CreateContainerAsync(config);

        Console.WriteLine($"容器创建完成: {response.ID}");

        // 启动容器
        var started = await _dockerClient.Containers.StartContainerAsync(response.ID, new ContainerStartParameters());
        if (!started)
        {
            throw new Exception($"容器启动失败: {response.ID}");
        }

        if (AgentContext.Current?.NotifyAction != null)
            await AgentContext.Current!.NotifyAction!.Invoke(new AgentConsoleProtocol
            {
                Id = id,
                type = "info",
                message = $"容器 {containerName} 部署成功，ID {response.ID}",
                code = 200,
                error = null
            });

        var logs = string.Empty;
        try
        {

            var token = new CancellationTokenSource(TimeSpan.FromSeconds(5));

            await _dockerClient.Containers.GetContainerLogsAsync(response.ID, new ContainerLogsParameters()
            {
                ShowStdout = true,
                ShowStderr = true,
                Follow = true
            }, token.Token, new Progress<string>((async s =>
            {
                Console.WriteLine(s);
                logs += s;
                await AgentContext.Current!.NotifyAction!.Invoke(new AgentConsoleProtocol
                {
                    Id = id,
                    type = "docker",
                    message = s,
                    code = 200,
                    error = null
                });
            })));

            await Task.Delay(5000);
        }
        finally
        {
            
        }

        Console.WriteLine($"容器启动成功: {response.ID}");
        return response.ID + "\n容器日志：\n" + logs;
    }

    /// <summary>
    /// 释放资源
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                _dockerClient?.Dispose();
            }

            _disposed = true;
        }
    }
}